package com.third.bank.scheduled;

import cn.hutool.core.date.TimeInterval;
import com.third.bank.Dto.InterestRateInfo;
import com.third.bank.Dto.InterestRecordsDto;
import com.third.bank.Dto.UserAndAmountBalance;
import com.third.bank.entity.*;
import com.third.bank.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.third.bank.common.constant.BusinessTypeConstant.INTEREST_SETTLEMENT_TYPE;
import static com.third.bank.common.constant.CommonConstant.DEFAULT_COUNTERPART_ACCOUNT;
import static com.third.bank.common.constant.CommonConstant.DEFAULT_COUNTERPART_NAME;

/**
 * @author hm
 * @version 1.0
 * @description TODO
 * @date 2023/4/19 16:21
 */
@Slf4j
@Component
public class InterestCalculationTask {

    @Autowired
    private InterestRecordsService interestRecordsService;

    @Autowired
    private UserBalanceDailyService userBalanceDailyService;

    @Autowired
    private InterestInfoService interestInfoService;

    @Autowired
    private TransactionRecordsService transactionRecordsService;

    @Autowired
    private BalanceService balanceService;

    @Autowired
    private UserService userService;


    @Scheduled(cron = "${bank.schedule.interestTime}")
    @Transactional
    public void interestCalculation() {
        // 1、获取当前时间和当前利率
        LocalDateTime now = LocalDateTime.now();
        InterestRateInfo currentInterestRateInfo = interestInfoService.getCurrentInterestRate(now);
        // 2、根据利息结算记录表中的上一次结息记录的截止时间和当前时间，算出本次结息周期
        List<InterestRecordsDto> interestRecordsDtoList = interestRecordsService.getLastInterestSettlementTime();
        // 3、获取用户日余额记录表
        List<UserBalanceDaily> userBalanceDailyList = userBalanceDailyService.list();
        Map<Long, List<UserBalanceDaily>> userBalanceDailyMap =
                userBalanceDailyList.stream()
                                    .collect(Collectors.groupingBy(UserBalanceDaily::getUserId));
        // 获取用户全部信息
        List<User> userList = userService.list();

        // 获取结算过利息的用户
        List<InterestRecordsDto> distinctInterestList = distinctUsers(userList, interestRecordsDtoList);

        // 计算用户结息时间和结息金额
        log.info("开始计算用户结息时间和结息金额");
        TimeInterval timeInterval = new TimeInterval();
        timeInterval.start();
        List<UserAndAmountBalance> userAndAmountBalanceList =
                distinctInterestList.stream().map(interestRecordsDto -> {
                    Long userId = interestRecordsDto.getUserId();
                    LocalDateTime latestEndTime = interestRecordsDto.getLatestEndTime();

                    List<UserBalanceDaily> userBalanceDailies = userBalanceDailyMap.get(userId);
                    List<UserBalanceDaily> availableUserBalanceDailies =
                            userBalanceDailies.stream()
                                              .filter(userBalanceDaily -> {
                                                  LocalDateTime dateTime = userBalanceDaily.getDateTime();
                                                  return dateTime.isAfter(latestEndTime) && dateTime.isBefore(now);
                                              }).collect(Collectors.toList());

                    BigDecimal totalBalance = BigDecimal.valueOf(0);
                    for (UserBalanceDaily userBalanceDaily : availableUserBalanceDailies) {
                        BigDecimal balance = userBalanceDaily.getBalance();
                        totalBalance = totalBalance.add(balance);
                    }
                    UserAndAmountBalance userAndAmountBalance = new UserAndAmountBalance();
                    userAndAmountBalance.setUserId(userId);
                    userAndAmountBalance.setLastEndTime(latestEndTime);
                    userAndAmountBalance.setAmountBalance(totalBalance);
                    return userAndAmountBalance;
                }).collect(Collectors.toList());
        log.info("结束计算用户结息时间和结息金额，总耗时 = {}ms", timeInterval.intervalRestart());

        // 5、计算利息并写入结算利息记录表
        log.info("开始计算利息");
        Long rateId = currentInterestRateInfo.getRateId();
        BigDecimal currentInterestRate = currentInterestRateInfo.getCurrentInterestRate();
        List<InterestRecords> interestRecordsList =
                userAndAmountBalanceList.stream().map(userAndAmountBalance -> {
                    LocalDateTime nowed = LocalDateTime.now();
                    LocalDateTime lastEndTime = userAndAmountBalance.getLastEndTime();
                    Long userId = userAndAmountBalance.getUserId();
                    BigDecimal amountBalance = userAndAmountBalance.getAmountBalance();
                    // 利息计算 用累计计息积数 * 年利率 / 360
                    BigDecimal interestAmount = amountBalance.multiply(currentInterestRate)
                                                             .divide(BigDecimal.valueOf(360), 2, RoundingMode.HALF_UP);
                    InterestRecords interestRecords = new InterestRecords();
                    interestRecords.setInterestRate(currentInterestRate);
                    interestRecords.setCreatedTime(nowed);
                    interestRecords.setInterestAmount(interestAmount);
                    interestRecords.setUserId(userId);
                    interestRecords.setStartTime(lastEndTime);
                    interestRecords.setEndTime(nowed);
                    interestRecords.setRateId(rateId);
                    return interestRecords;
                }).collect(Collectors.toList());

        log.info("结束计算利息, 总耗时 = {}ms", timeInterval.interval());

        interestRecordsService.saveBatch(interestRecordsList);
        // 7、更新用户余额
        LocalDateTime balanceNow = LocalDateTime.now();
        List<Balance> balanceList = interestRecordsList.stream().map(interestRecords -> {
            Long userId = interestRecords.getUserId();
            BigDecimal interestAmount = interestRecords.getInterestAmount();
            Balance balance = new Balance();
            balance.setUserId(userId);
            balance.setBalance(interestAmount);
            balance.setUpdateTime(balanceNow);
            return balance;
        }).collect(Collectors.toList());
        balanceService.updateBatchUserBalance(balanceList);

        // 8、查询用户余额
        List<Balance> balances = balanceService.list();

        // 9、记录用户账务交易流水表
        Map<Long, BigDecimal> userIdAndInterest = getUserIdAndInterest(interestRecordsList);
        LocalDateTime dateTime = LocalDateTime.now();
        List<TransactionRecords> transactionRecordsList = balances.stream().map(balance -> {
            Long userId = balance.getUserId();
            TransactionRecords transactionRecords = new TransactionRecords();
            transactionRecords.setUserId(userId);
            transactionRecords.setTransactionTime(balance.getUpdateTime());
            transactionRecords.setBusinessType(INTEREST_SETTLEMENT_TYPE);
            transactionRecords.setTransactionAmount(userIdAndInterest.get(userId));
            transactionRecords.setAfterBalance(balance.getBalance());
            transactionRecords.setCounterpartName(DEFAULT_COUNTERPART_NAME);
            transactionRecords.setCounterpartAccount(DEFAULT_COUNTERPART_ACCOUNT);
            transactionRecords.setCreatedTime(dateTime);
            return transactionRecords;
        }).collect(Collectors.toList());

        transactionRecordsService.saveBatch(transactionRecordsList);

    }

    /**
     * 处理尚未结算过利息的用户，并整合到结息表账户截止时间List
     *
     * @param userList               用户信息List
     * @param interestRecordsDtoList 结息表账户截止时间List
     * @return
     */
    private List<InterestRecordsDto> distinctUsers(List<User> userList, List<InterestRecordsDto> interestRecordsDtoList) {
        List<InterestRecordsDto> list = userList.stream().map(user -> {
            InterestRecordsDto interestRecordsDto = new InterestRecordsDto();
            interestRecordsDto.setUserId(user.getId());
            interestRecordsDto.setLatestEndTime(user.getCreateTime());
            return interestRecordsDto;
        }).collect(Collectors.toList());

        Map<Long, LocalDateTime> timeMap =
                interestRecordsDtoList.stream()
                                      .collect(Collectors.toMap(InterestRecordsDto::getUserId, InterestRecordsDto::getLatestEndTime));

        list.forEach(interestRecordsDto -> {
            Long userId = interestRecordsDto.getUserId();
            if (!timeMap.containsKey(userId)) {
                interestRecordsDtoList.add(interestRecordsDto);
            }
        });

        return interestRecordsDtoList;

    }


    /**
     * 获取用户和利息金额的关系
     *
     * @param interestRecordsList 利息结算记录集合
     * @return 用户和利息金额的关系
     */
    private Map<Long, BigDecimal> getUserIdAndInterest(List<InterestRecords> interestRecordsList) {
        return interestRecordsList.stream()
                                  .collect(Collectors.toMap(InterestRecords::getUserId, InterestRecords::getInterestAmount));
    }


    /**
     * 找出所有用户最早一次日记金额的时间
     *
     * @param interestRecordsDtoList 利息结算记录集合
     * @return 所有用户最早一次日记金额的时间
     */
    private Map<Long, LocalDateTime> getMinDailyTime(List<InterestRecordsDto> interestRecordsDtoList) {
        return interestRecordsDtoList.stream()
                                     .collect(Collectors.toMap(InterestRecordsDto::getUserId, InterestRecordsDto::getLatestEndTime, (oldValue, newValue) -> oldValue.isBefore(newValue) ? oldValue : newValue));
    }
}
